package com.frank.oj.manager;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.frank.model.entity.judge.Judge;
import com.frank.model.entity.training.Training;
import com.frank.model.entity.training.TrainingCategory;
import com.frank.model.entity.training.TrainingProblem;
import com.frank.model.entity.training.TrainingRegister;
import com.frank.oj.common.exception.StatusAccessDeniedException;
import com.frank.oj.common.exception.StatusFailException;
import com.frank.oj.common.exception.StatusForbiddenException;
import com.frank.oj.constant.Constants;
import com.frank.oj.model.bo.Pair_;
import com.frank.oj.model.dto.RegisterTrainingDTO;
import com.frank.oj.model.vo.*;
import com.frank.oj.service.entity.group.GroupMemberEntityService;
import com.frank.oj.service.entity.judge.JudgeEntityService;
import com.frank.oj.service.entity.training.*;
import com.frank.oj.service.entity.user.UserInfoEntityService;
import com.frank.oj.shiro.AccountProfile;
import com.frank.oj.utils.RedisUtils;
import com.frank.oj.validator.GroupValidator;
import com.frank.oj.validator.TrainingValidator;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author frank
 * @Date 2024/4/5
 */
@Component
public class TrainingManager {
    @Resource
    private TrainingEntityService trainingEntityService;

    @Resource
    private TrainingRegisterEntityService trainingRegisterEntityService;

    @Resource
    private TrainingCategoryEntityService trainingCategoryEntityService;

    @Resource
    private TrainingProblemEntityService trainingProblemEntityService;

    @Resource
    private TrainingRecordEntityService trainingRecordEntityService;

    @Resource
    private UserInfoEntityService userInfoEntityService;

    @Resource
    private AdminTrainingRecordManager adminTrainingRecordManager;

    @Resource
    private GroupMemberEntityService groupMemberEntityService;

    @Resource
    private JudgeEntityService judgeEntityService;

    @Resource
    private GroupValidator groupValidator;

    @Resource
    private TrainingValidator trainingValidator;
    @Resource
    private RedisUtils redisUtils;

    public void checkAndSyncTrainingRecord(Long pid, Long submitId, String uid) {
        List<TrainingProblem> trainingProblemList = trainingProblemEntityService.getPrivateTrainingProblemLisByPid(pid, uid);
    }

    public TrainingVO getTraining(Long tid) throws StatusFailException, StatusAccessDeniedException, StatusForbiddenException {
        AccountProfile userRole = (AccountProfile) SecurityUtils.getSubject().getPrincipal();
        boolean isRoot = SecurityUtils.getSubject().hasRole("root");

        Training training = trainingEntityService.getById(tid);
        if (training == null || !training.getStatus()) {
            throw new StatusFailException("该训练不存在或已被封禁！");
        }

        Long gid = training.getGid();
        if (training.getIsGroup()) {
            if (!isRoot && !groupValidator.isGroupMember(userRole.getUid(), gid)) {
                throw new StatusForbiddenException("您没有权限查看该训练！");
            }
        } else {
            gid = null;
        }

        TrainingVO trainingVO = BeanUtil.copyProperties(training, TrainingVO.class);
        TrainingCategory trainingCategory = trainingCategoryEntityService.getTrainingCategoryByTrainingId(training.getId());
        trainingVO.setCategoryName(trainingCategory.getName());
        trainingVO.setCategoryColor(trainingCategory.getColor());

        List<Long> trainingProblemIdList = trainingProblemEntityService.getTrainingProblemIdList(training.getId());
        trainingVO.setProblemCount(trainingProblemIdList.size());

        if (userRole != null && trainingValidator.isInTrainingOrAdmin(training, userRole)) {
            Integer count = trainingProblemEntityService.getUserTrainingACProblemCount(userRole.getUid(), gid, trainingProblemIdList);
            trainingVO.setAcCount(count);
        } else {
            trainingVO.setAcCount(0);
        }

        return trainingVO;
    }

    public List<ProblemVO> getTrainingProblemList(Long tid) throws StatusFailException, StatusForbiddenException, StatusAccessDeniedException {

        Training training = trainingEntityService.getById(tid);
        if (training == null || !training.getStatus()) {
            throw new StatusFailException("该训练不存在或已被封禁！");
        }
        trainingValidator.validateTrainingAuth(training);

        return trainingProblemEntityService.getTrainingProblemList(tid);
    }

    public AccessVO getTrainingAccess(Long tid) throws StatusFailException {
        AccountProfile userRole = (AccountProfile) SecurityUtils.getSubject().getPrincipal();

        LambdaQueryWrapper<TrainingRegister> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TrainingRegister::getTid, tid).eq(TrainingRegister::getUid, userRole.getUid());
        TrainingRegister trainingRegister = trainingRegisterEntityService.getOne(queryWrapper);

        boolean access = false;
        if (trainingRegister != null) {
            access = true;
            Training training = trainingEntityService.getById(tid);
            if (training == null || !training.getStatus()) {
                throw new StatusFailException("该训练不存在或已被封禁！");
            }
        }
        AccessVO accessVO = new AccessVO();
        accessVO.setAccess(access);
        return accessVO;
    }

    public void toRegisterTraining(RegisterTrainingDTO registerTrainingDto) throws StatusFailException, StatusForbiddenException {
        AccountProfile userRole = (AccountProfile) SecurityUtils.getSubject().getPrincipal();

        Long tid = registerTrainingDto.getTid();
        String password = registerTrainingDto.getPassword();

        if (tid == null || StringUtils.isEmpty(password)) {
            throw new StatusFailException("参数错误！");
        }

        Training training = trainingEntityService.getById(tid);
        if (training == null || !training.getStatus()) {
            throw new StatusFailException("该训练不存在或已被封禁！");
        }

        // 注册key
        String key = "try-registerTraining-num:" + userRole.getUid() + "_" + tid;

        Integer tryLoginCount = (Integer) redisUtils.get(key);

        if (tryLoginCount != null && tryLoginCount >= 5) {
            throw new StatusForbiddenException("尝试次数过多，请半小时后重试！😏（请联系创建人获取邀请码，不要尝试破解）");
        }

        if (!training.getPrivatePwd().equals(password)) {
            if (tryLoginCount == null) {
                redisUtils.set(key, 1, 60 * 30);
            } else {
                redisUtils.set(key, tryLoginCount + 1, 60 * 30);
            }
            throw new StatusFailException("邀请码错误！请重试");
        }

        LambdaQueryWrapper<TrainingRegister> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TrainingRegister::getTid, tid).eq(TrainingRegister::getUid, userRole.getUid());
        if (trainingRegisterEntityService.count(queryWrapper) > 0) {
            throw new StatusFailException("您已加入该训练，请勿重复注册！");
        }

        boolean saved = trainingRegisterEntityService.save(new TrainingRegister()
                .setTid(tid)
                .setUid(userRole.getUid()));

        if (saved) {
            if (tryLoginCount != null) {
                redisUtils.del(key);
            }
            adminTrainingRecordManager.syncUserSubmissionToRecordByTid(tid, userRole.getUid());
        } else {
            throw new StatusFailException("加入失败！");
        }
    }

    public IPage<TrainingRankVO> getTrainingRank(Long tid, Integer limit, Integer currentPage, String keyword) throws StatusFailException, StatusForbiddenException, StatusAccessDeniedException {
        if (currentPage == null || currentPage < 1) currentPage = 1;
        if (limit == null || limit < 1) limit = 30;

        Training training = trainingEntityService.getById(tid);

        if (training == null || !training.getStatus()) {
            throw new StatusFailException("该训练不存在或已被封禁！");
        }
        trainingValidator.validateTrainingAuth(training);

        if (StrUtil.isNotBlank(keyword)) {
            keyword = keyword.toLowerCase();
        }

        // todo 优化：将训练创建人姓名改为id，放重复
        return getTrainingRankings(tid, training.getIsGroup() ? training.getGid() : null, training.getAuthor(), currentPage, limit, keyword);
    }

    /**
     * 获取训练排名列表
     *
     * @param tid         训练ID
     * @param gid         组ID，可为空，为空时表示获取所有人的排名
     * @param username    当前用户用户名，用于过滤掉自身排名
     * @param currentPage 当前页码
     * @param limit       每页显示数量
     * @param keyword     搜索关键词，用于模糊匹配用户名、真实姓名、学校
     * @return 返回分页后的训练排名列表
     */
    private IPage<TrainingRankVO> getTrainingRankings(Long tid, Long gid, String username, Integer currentPage, Integer limit, String keyword) {
        // 获取题目的ID与显示ID的映射关系
        Map<Long, String> tpIdMapDisplayId = getTPIdMapDisplayId(tid);
        // 获取该训练的所有记录
        List<TrainingRecordVO> trainingRecordVOList = trainingRecordEntityService.getTrainingRecord(tid);

        // 获取超级管理员的用户ID列表 ：root
        List<String> superAdminUidList = userInfoEntityService.getSuperAdminUidList(); // 超级管理员1000

        if (gid != null) {
            List<String> groupRootUidList = groupMemberEntityService.getGroupRootUidList(gid);
            superAdminUidList.addAll(groupRootUidList);
        }

        // 初始化结果列表、用户ID与排名索引的映射关系
        List<TrainingRankVO> result = new ArrayList<>();
        HashMap<String, Integer> uidMapIndex = new HashMap<>();
        int pos = 0;
        // 遍历所有训练记录，构建排名列表
        for (TrainingRecordVO trainingRecordVO : trainingRecordVOList) {
            // todo 过滤掉超级管理员和训练创建人的记录
            //if (username.equals(trainingRecordVO.getUsername()) || superAdminUidList.contains(trainingRecordVO.getUid())) {
            //    continue;
            //}

            // 若有搜索关键词，则进行匹配过滤
            if (StrUtil.isNotBlank(keyword)) {
                boolean isMatchKeyword = matchKeywordIgnoreCase(keyword, trainingRecordVO.getUsername())
                        || matchKeywordIgnoreCase(keyword, trainingRecordVO.getRealname())
                        || matchKeywordIgnoreCase(keyword, trainingRecordVO.getSchool());
                if (!isMatchKeyword) {
                    continue;
                }
            }

            // 处理用户的排名信息
            TrainingRankVO trainingRankVO;
            Integer index = uidMapIndex.get(trainingRecordVO.getUid());
            if (index == null) {
                // 新用户，初始化排名信息
                trainingRankVO = new TrainingRankVO();
                // 设置用户基本信息
                trainingRankVO.setRealname(trainingRecordVO.getRealname())
                        .setAvatar(trainingRecordVO.getAvatar())
                        .setSchool(trainingRecordVO.getSchool())
                        .setGender(trainingRecordVO.getGender())
                        .setUid(trainingRecordVO.getUid())
                        .setUsername(trainingRecordVO.getUsername())
                        .setNickname(trainingRecordVO.getNickname())
                        .setAc(0)
                        .setTotalRunTime(0);

                // 初始化提交信息
                HashMap<String, HashMap<String, Object>> submissionInfo = new HashMap<>();
                trainingRankVO.setSubmissionInfo(submissionInfo);

                result.add(trainingRankVO);
                uidMapIndex.put(trainingRecordVO.getUid(), pos);
                pos++;
            } else {
                // 已有用户，直接获取
                trainingRankVO = result.get(index);
            }
            // 获取题目的显示ID
            String displayId = tpIdMapDisplayId.get(trainingRecordVO.getTpid());
            // 处理该用户的题目提交信息
            HashMap<String, Object> problemSubmissionInfo = trainingRankVO
                    .getSubmissionInfo()
                    .getOrDefault(displayId, new HashMap<>());

            // 更新用户的AC状态和总耗时
            if ((Boolean) problemSubmissionInfo.getOrDefault("isAC", false)) {
                if (trainingRecordVO.getStatus().intValue() == Constants.Judge.STATUS_ACCEPTED.getStatus()) {
                    int runTime = (int) problemSubmissionInfo.getOrDefault("runTime", 0);
                    if (runTime > trainingRecordVO.getUseTime()) {
                        trainingRankVO.setTotalRunTime(trainingRankVO.getTotalRunTime() - runTime + trainingRecordVO.getUseTime());
                        problemSubmissionInfo.put("runTime", trainingRecordVO.getUseTime());
                    }
                }
                continue;
            }
            // 更新提交状态和分数
            problemSubmissionInfo.put("status", trainingRecordVO.getStatus());
            problemSubmissionInfo.put("score", trainingRecordVO.getScore());

            // 更新AC次数、该题目的AC状态和耗时
            if (trainingRecordVO.getStatus().intValue() == Constants.Judge.STATUS_ACCEPTED.getStatus()) {
                trainingRankVO.setAc(trainingRankVO.getAc() + 1);
                problemSubmissionInfo.put("isAC", true);
                problemSubmissionInfo.put("runTime", trainingRecordVO.getUseTime());
                trainingRankVO.setTotalRunTime(trainingRankVO.getTotalRunTime() + trainingRecordVO.getUseTime());
            }

            // 更新提交信息
            trainingRankVO.getSubmissionInfo().put(displayId, problemSubmissionInfo);
        }

        // 根据AC数和总耗时对排名列表进行排序
        List<TrainingRankVO> orderResultList = result.stream().sorted(Comparator.comparing(TrainingRankVO::getAc, Comparator.reverseOrder())
                .thenComparing(TrainingRankVO::getTotalRunTime)
        ).collect(Collectors.toList());

        // 分页处理
        Page<TrainingRankVO> page = new Page<>(currentPage, limit);
        int count = orderResultList.size();
        List<TrainingRankVO> pageList = new ArrayList<>();

        int currId = currentPage > 1 ? (currentPage - 1) * limit : 0; // 当前页起始索引

        // 构建当前页的排名列表
        for (int i = 0; i < limit && i < count - currId; i++) {
            pageList.add(orderResultList.get(currId + i));
        }
        // 设置分页信息
        page.setSize(limit);
        page.setCurrent(currentPage);
        page.setTotal(count);
        page.setRecords(pageList);
        return page;
    }


    private boolean matchKeywordIgnoreCase(String keyword, String content) {
        return content != null && content.toLowerCase().contains(keyword);
    }

    /**
     * 获取训练题目的展示id
     */
    private Map<Long, String> getTPIdMapDisplayId(Long tid) {
        LambdaQueryWrapper<TrainingProblem> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TrainingProblem::getTid, tid);

        List<TrainingProblem> trainingProblemList = trainingProblemEntityService.list(queryWrapper);

        return trainingProblemList.stream().collect(Collectors.toMap(TrainingProblem::getId, TrainingProblem::getDisplayId));
    }

    public List<ProblemFullScreenListVO> getProblemFullScreenList(Long tid) throws StatusFailException, StatusForbiddenException, StatusAccessDeniedException {
        Training training = trainingEntityService.getById(tid);
        if (training == null || !training.getStatus()) {
            throw new StatusFailException("该训练不存在或不允许显示！");
        }
        trainingValidator.validateTrainingAuth(training);
        List<ProblemFullScreenListVO> problemList = trainingProblemEntityService.getTrainingFullScreenProblemList(tid);

        List<Long> pidList = problemList.stream().map(ProblemFullScreenListVO::getPid).collect(Collectors.toList());
        AccountProfile userRolesVo = (AccountProfile) SecurityUtils.getSubject().getPrincipal();

        QueryWrapper<Judge> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("distinct pid,status,score,submit_time")
                .in("pid", pidList)
                .eq("uid", userRolesVo.getUid())
                .orderByDesc("submit_time");

        queryWrapper.eq("cid", 0);
        if (training.getGid() != null && training.getIsGroup()) {
            queryWrapper.eq("gid", training.getGid());
        } else {
            queryWrapper.isNull("gid");
        }
        List<Judge> judges = judgeEntityService.list(queryWrapper);
        HashMap<Long, Pair_<Integer, Integer>> pidMap = new HashMap<>();
        for (Judge judge : judges) {
            if (Objects.equals(judge.getStatus(), Constants.Judge.STATUS_PENDING.getStatus())
                    || Objects.equals(judge.getStatus(), Constants.Judge.STATUS_COMPILING.getStatus())
                    || Objects.equals(judge.getStatus(), Constants.Judge.STATUS_JUDGING.getStatus())) {
                continue;
            }
            if (judge.getStatus().intValue() == Constants.Judge.STATUS_ACCEPTED.getStatus()) {
                // 如果该题目已通过，则强制写为通过（0）
                pidMap.put(judge.getPid(), new Pair_<>(judge.getStatus(), judge.getScore()));
            } else if (!pidMap.containsKey(judge.getPid())) {
                // 还未写入，则使用最新一次提交的结果
                pidMap.put(judge.getPid(), new Pair_<>(judge.getStatus(), judge.getScore()));
            }
        }
        for (ProblemFullScreenListVO problemVO : problemList) {
            Pair_<Integer, Integer> pair_ = pidMap.get(problemVO.getPid());
            if (pair_ != null) {
                problemVO.setStatus(pair_.getKey());
                problemVO.setScore(pair_.getValue());
            }
        }
        return problemList;
    }
}
